package chinaums

import (
	"fmt"
	"sort"
	"errors"
	"strings"
	"crypto/md5"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/url"
	"bytes"
	"thirdpaysdk/utils/mapstruct"
	"github.com/astaxie/beego"
)

type Auth struct {
	//merchanNo   string
	appsecret string
	requrl      string
}

func NewSDK(restURL,appsecret string) *Auth {
	return &Auth{requrl: restURL, appsecret: appsecret}
}

func (this *Auth) RequestMicroApp(params map[string]interface{},response interface{}) (map[string]interface{},error){
	params["sign"] = MakeSign(params, this.appsecret)
	body, err := json.Marshal(params)
	if err != nil {
		return nil, err
	}
	beego.Debug("chinaums request req1:", string(body))
	req, err := http.NewRequest("POST", this.requrl, bytes.NewBuffer(body))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json") //必须
	req.Header.Set("Accept", "application/json") //必须
	client := &http.Client{}
	
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	beego.Debug("chinaums rsp1 body:", string(data))
	err = json.Unmarshal(data, &response)
	if err != nil {
	    beego.Debug("chinaums rsp1 err:", err.Error())
	    return nil, err
	}
	rspMap := mapstruct.Struct2Map(response, "json")
	if rspMap["errCode"] != "SUCCESS" {
	    returnMsg := rspMap["errCode"].(string) + ":" + rspMap["errMsg"].(string)
		err = errors.New(returnMsg)
		beego.Debug("chinaums rsp1 err:", err.Error())
		return nil, err
	}
	// 校验回执签名
	sign := MakeSign(rspMap, this.appsecret)
	//beego.Debug("calc sign:", sign)
	if rspMap["sign"] != sign {
		err = errors.New("response sign err")
		beego.Debug("chinaums rsp1 err:", err.Error())
		return nil, err
	}
	return rspMap, nil
}

func (this *Auth) RequestOther(params map[string]interface{}) (map[string]interface{},error){
	params["sign"] = MakeSign(params, this.appsecret)
	body, err := json.Marshal(params)
	if err != nil {
		return nil, err
	}
	beego.Debug("chinaums request req2:", string(body))
	req, err := http.NewRequest("POST", this.requrl, bytes.NewBuffer(body))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json") //必须
	req.Header.Set("Accept", "application/json") //必须
	client := &http.Client{}
	rspMap := make(map[string]interface{})
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	beego.Debug("chinaums rsp2 body:", string(data))
	err = json.Unmarshal(data, &rspMap)
	if err != nil {
	    beego.Debug("chinaums rsp2 err:", err.Error())
		return nil, err
	}
	if rspMap["errCode"] != "SUCCESS" {
	    returnMsg := rspMap["errCode"].(string) + ":" + rspMap["errMsg"].(string)
		err = errors.New(returnMsg)
		beego.Debug("chinaums rsp2 err:", err.Error())
		return nil, err
	}
	//
	sign := MakeSign(rspMap, this.appsecret)
	//beego.Debug("calc sign:", sign)
	if rspMap["sign"] != sign {
		err = errors.New("response sign err")
		beego.Debug("chinaums rsp2 err:", err.Error())
		return nil, err
	}
	return rspMap, nil
}

func MakeSign(params map[string]interface{},key string) string {
	paramsNew := make(map[string]string)
	for k, v := range params {
		if k == "miniPayRequest" {
            paramsNew[k] = fmt.Sprintf("%v", string(v.(json.RawMessage)))
		} else if k == "subOrders" {
            b, _ := json.Marshal(v)
			paramsNew[k] = string(b)
		} else {
            paramsNew[k] = fmt.Sprintf("%v", v)
		}
	}
	var keys []string
	var sorted []string
	for k, v := range paramsNew {
		if k != "sign" && v != "" {
			keys = append(keys, k)
		}
	}
	sort.Strings(keys)
	for _, k := range keys {
		sorted = append(sorted, fmt.Sprintf("%s=%s", k, paramsNew[k]))
	}
	str := strings.Join(sorted, "&")
	//beego.Debug("req sign str:", str)
	str += key
	return fmt.Sprintf("%X", md5.Sum([]byte(str)))
}

func NotifyMakeSign(params map[string]interface{}, key string) string {
	paramsNew := make(map[string]string)
	for k, v := range params {
		paramsNew[k] = fmt.Sprintf("%v", v)
	}
	var keys []string
	var sorted []string
	for k, v := range paramsNew {
		if k != "sign" && v != "" {
			keys = append(keys, k)
		}
	}
	sort.Strings(keys)
	for _, k := range keys {
	    m, _:= url.QueryUnescape(paramsNew[k])
		sorted = append(sorted, fmt.Sprintf("%s=%s", k, m))
	}
	str := strings.Join(sorted, "&")
	str += key
	//beego.Debug("str:", str)
	return fmt.Sprintf("%X", md5.Sum([]byte(str)))
}


